﻿
//
//
//

namespace Microsoft.OpenTurf.Silverlight.Common
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ServiceModel;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using Microsoft.Maps.MapControl;
    using Microsoft.OpenTurf.Silverlight.Common.SearchPlatformServices;
    using Microsoft.OpenTurf.Silverlight.Shared;
    using System.Windows.Shapes;

    public class BingMapHelper
    {
        private static IEnumerator<Color> colors;

        #region Properties
        private PlatformServices.GeocodeServiceClient geocodeClient;
        private PlatformServices.GeocodeServiceClient GeocodeClient
        {
            get
            {
                if (null == geocodeClient)
                {
                    BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
                    UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/GeocodeService/GeocodeService.svc");

                    //Create the Service Client
                    geocodeClient = new PlatformServices.GeocodeServiceClient(binding, new EndpointAddress(serviceUri.Uri));
                    geocodeClient.GeocodeCompleted += new EventHandler<PlatformServices.GeocodeCompletedEventArgs>(Client_GeoCodeCompleted);
                }
                return geocodeClient;
            }
        }

        private SearchPlatformServices.SearchServiceClient searchClient;
        private SearchPlatformServices.SearchServiceClient SearchClient
        {
            get
            {
                if (null == searchClient)
                {
                    BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
                    binding.MaxReceivedMessageSize = int.MaxValue;
                    binding.MaxBufferSize = int.MaxValue;

                    UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/SearchService/SearchService.svc");

                    // Create the Service Client
                    searchClient = new SearchPlatformServices.SearchServiceClient(binding, new EndpointAddress(serviceUri.Uri));
                    searchClient.SearchCompleted += new EventHandler<SearchPlatformServices.SearchCompletedEventArgs>(Client_SearchCompleted);
                }
                return searchClient;
            }
        }
        #endregion

        #region static members
        public static MapLayer CreateMapLayer()
        {
            return new MapLayer();
        }
        public static MapItemsControl CreateMapItemsControl()
        {
            return new MapItemsControl();
        }
        public static MapPolygon CreateMapPolygon()
        {
            return new MapPolygon();
        }

        private static Pushpin CreatePushPin(string name, Uri backgroundUri, Location location)
        {
            Pushpin pin = new Pushpin();

            pin.Name = name;
            pin.Location = location;
            try
            {
                pin.Background = new ImageBrush() { ImageSource = new BitmapImage(backgroundUri) };
            }
            catch (Exception)
            {
                // Could implement a Default image here.
                //pin.Background = new ImageBrush() {};
            }

            return pin;
        }
        private static Pushpin CreatePushPin(string tag, Location location)
        {
            Pushpin pin = new Pushpin();

            pin.Location = location;
            pin.Tag = tag;

            return pin;
        }
        private static MapPolygon CreateMapPolygon(IFeedItem feedItem)
        {
            MapPolygon polygon = CreateMapPolygon();

            polygon.Name = feedItem.ID;
            polygon.Locations = new LocationCollection();

            foreach (var coord in feedItem.Coordinates)
            {
                Location loc = new Location()
                {
                    Latitude = coord.Latitude,
                    Longitude = coord.Longitude
                };

                polygon.Locations.Add(loc);
            }

            return polygon;
        }

        public static MapLayer CreatePinLayer(IEnumerable<IFeedItem> feedList)
        {
            var layer = CreateMapLayer();

            // Create a Pushpin for each Feed Item.
            foreach (IFeedItem item in feedList)
            {
                // We can't add a pin without a location.
                if (item.Coordinates == null || item.Coordinates.Count == 0) continue;

                MapPoint point = item.Coordinates[0];
                Location location = new Location(point.Latitude, point.Longitude);

                Pushpin pin = CreatePushPin(item.ID, item.Icon, location);

                string tooltip = item.Title + "\n" + item.Description;
                ToolTipService.SetToolTip(pin, tooltip);

                layer.AddChild(pin, location, PositionOrigin.BottomCenter);
            }

            return layer;
        }
        public static MapLayer CreateCustomPinLayer(IFeed feed)
        {
            MapLayer layer = CreateMapLayer();

            BitmapImage icon = new BitmapImage(feed.Icon);

            foreach (IFeedItem item in feed.FeedItems)
            {
                // We can't add a pin without a location.
                if (item.Coordinates == null || item.Coordinates.Count == 0) continue;

                MapPoint point = item.Coordinates[0];
                Location location = new Location(point.Latitude, point.Longitude, 5.0);

                Image image = new Image();
                image.Source = icon;
                image.Opacity = AppGlobals.DEFAULT_OPACITY;
                image.Stretch = Stretch.None;
                image.Visibility = System.Windows.Visibility.Visible;
                image.Width = AppGlobals.DEFAULT_CUSTOM_PIN_WIDTH;
                image.Height = AppGlobals.DEFAULT_CUSTOM_PIN_HEIGHT;

                layer.AddChild(image, location);
            }

            return layer;
        }
        public static MapLayer CreateMapPolygonLayer(IEnumerable<IFeedItem> feedList)
        {
            MapLayer layer = CreateMapLayer();

            foreach (IFeedItem item in feedList)
            {
                // We can't add a polygon without a location.
                if (item.Coordinates == null || item.Coordinates.Count == 0) continue;

                MapPolygon polygon = CreateMapPolygon(item);

                SolidColorBrush fillBrush = CreateSolidBrushWithCurrentColor();
                polygon.Fill = fillBrush;
                polygon.Stroke = fillBrush;
                polygon.StrokeThickness = AppGlobals.DEFAULT_POLYGON_STROKE_THICKNESS;
                polygon.Opacity = AppGlobals.DEFAULT_OPACITY;

                string tooltip = item.Title + "\n" + item.Description;
                ToolTipService.SetToolTip(polygon, tooltip);

                layer.Children.Add(polygon);
            }
            return layer;
        }
        private static SolidColorBrush CreateSolidBrushWithCurrentColor()
        {
            // If System Colors aren't loaded, load them up.
            if (colors == null) LoadColorPallette();

            TryNextColor();
            if (colors.Current == Colors.Transparent)
                TryNextColor();

            // Make a new brush with the current color.
            return new SolidColorBrush(colors.Current);
        }

        private static void TryNextColor()
        {
            /*
             * Check to make sure we're not at the beginning
             * or the end of the Enumerator. If at the end,
             * reset to recycle through the colors.
             */
            if (!colors.MoveNext())
            {
                colors.Reset();
                colors.MoveNext();
            }
        }
        private static void LoadColorPallette()
        {
            List<Color> colorList = new List<Color>();
            colorList.Add(Colors.Blue);
            colorList.Add(Colors.Brown);
            colorList.Add(Colors.Cyan);
            colorList.Add(Colors.DarkGray);
            colorList.Add(Colors.Green);
            colorList.Add(Colors.LightGray);
            colorList.Add(Colors.Magenta);
            colorList.Add(Colors.Orange);
            colorList.Add(Colors.Purple);
            colorList.Add(Colors.Red);
            colorList.Add(Colors.Yellow);
            colors = colorList.GetEnumerator();
        }
        #endregion

        #region public members
        /// <summary>
        /// Locates an Address and Provides the Address details for Map Repositioning
        /// </summary>
        /// <param name="address">User Provided Address details</param>
        /// <param name="culture">the current culture of the map</param>
        /// <param name="credentials">Your Bing API Credentials</param>
        /// /// <remarks>Retrieve Data by creating an Event Handler for LocateAddressCompleted</remarks>
        public void LocateAddress(string address, string culture, Credentials credentials)
        {
            PlatformServices.GeocodeRequest request = new PlatformServices.GeocodeRequest();
            request.Culture = culture;
            request.Query = address;

            // Don't raise exceptions.
            request.ExecutionOptions = new PlatformServices.ExecutionOptions();
            request.ExecutionOptions.SuppressFaults = true;

            // Only accept results with high confidence.
            request.Options = new PlatformServices.GeocodeOptions();
            request.Options.Filters = new ObservableCollection<PlatformServices.FilterBase>();

            PlatformServices.ConfidenceFilter filter = new PlatformServices.ConfidenceFilter();
            filter.MinimumConfidence = PlatformServices.Confidence.High;
            request.Options.Filters.Add(filter);

            //Pass in credentials for web services call.
            request.Credentials = credentials;

            // Make asynchronous call to fetch the data
            GeocodeClient.GeocodeAsync(request, address);
        }
        /// <summary>
        /// Performs a search against the Bing Maps Search API
        /// </summary>
        /// <param name="query">User provided Search term</param>
        /// <param name="searchBoundaries">The BoundingRectangle of the map.</param>
        /// <param name="culture">The Culture of the map</param>
        /// <param name="credentials">Your API Credentials</param>
        /// <remarks>Retrieve Data by creating an Event Handler for SearchCompleted</remarks>
        public void Search(string query, LocationRect searchBoundaries, string culture, Credentials credentials)
        {
            SearchPlatformServices.SearchRequest request = new SearchPlatformServices.SearchRequest();
            request.Culture = culture;
            request.Query = query;

            // Don't raise exceptions.
            request.ExecutionOptions = new SearchPlatformServices.ExecutionOptions();
            request.ExecutionOptions.SuppressFaults = true;

            LocationRect mapBounds = new LocationRect(searchBoundaries);
            request.UserProfile = new SearchPlatformServices.UserProfile();
            request.UserProfile.MapView = mapBounds;

            //Pass in credentials for web services call.
            request.Credentials = credentials;

            //execute the request
            SearchClient.SearchAsync(request);
        }
        #endregion

        #region Callbacks
        private void Client_GeoCodeCompleted(object sender, PlatformServices.GeocodeCompletedEventArgs e)
        {
            GeoCodeRequestCompletedEventArgs args = null;

            if (e.Result.ResponseSummary.StatusCode == PlatformServices.ResponseStatusCode.Success && e.Result.Results.Count > 0)
            {
                args = new GeoCodeRequestCompletedEventArgs();

                args.DisplayName = e.Result.Results[0].DisplayName;
                args.Address = e.Result.Results[0].Address.FormattedAddress;
                args.BestView = e.Result.Results[0].BestView;

                foreach (var loc in e.Result.Results[0].Locations)
                {
                    args.Locations.Add(new Location(loc.Latitude, loc.Longitude, loc.Altitude));
                }
            }

            OnGeoCodeRequestCompleted(args);
        }
        private void Client_SearchCompleted(object sender, SearchPlatformServices.SearchCompletedEventArgs e)
        {
            SearchRequestCompletedEventArgs args = null;

            try
            {
                if (e.Result.ResponseSummary.StatusCode == ResponseStatusCode.Success)
                {
                    IDictionary<string, SearchResultBase> searchResults = new Dictionary<string, SearchResultBase>();
                    IList<Location> locations = new List<Location>();
                    MapItemsControl searchResultMapLayer = CreateMapItemsControl();

                    if ((e.Result != null) && (e.Result.ResultSets.Count > 0))
                    {
                        foreach (SearchResultBase result in e.Result.ResultSets[0].Results)
                        {
                            searchResults.Add(result.Id, result);

                            if (result.LocationData.Locations.Count > 0)
                            {
                                Location location = new Location(result.LocationData.Locations[0]);
                                searchResultMapLayer.Items.Add(CreatePushPin(result.Id, location));
                            }
                        }

                        args = new SearchRequestCompletedEventArgs();

                        args.SearchResults = searchResults;
                        args.SearchResultLayer = searchResultMapLayer;
                        args.Locations = locations;
                        args.BindingArea = e.Result.ResultSets[0].SearchRegion.BoundingArea as LocationRect;
                    }
                }
            }
            catch (Exception)
            {

            }

            OnSearchRequestCompleted(args);
        }
        #endregion

        #region events
        public event GeoCodeRequestCompletedEventHandler LocateAddressCompleted;
        private void OnGeoCodeRequestCompleted(GeoCodeRequestCompletedEventArgs e)
        {
            if (LocateAddressCompleted != null)
                LocateAddressCompleted(e);
        }
        public event SearchRequestCompletedEventHandler SearchCompleted;
        private void OnSearchRequestCompleted(SearchRequestCompletedEventArgs e)
        {
            if (SearchCompleted != null)
                SearchCompleted(e);
        }
        #endregion
    }
}